﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using NGenerics.UI.DataStructures.Trees;
using NGenerics.DataStructures.Trees;

using System.Drawing.Design;
using System.Drawing;
using System.Resources;
using System.Windows.Forms.Design;

namespace Layers.UI
{
    public abstract class TreeDirector
    {
        protected TreeView _view;
        protected bool _showRoot;

        public bool ShowRoot { get { return _showRoot; } set { _showRoot = value; Update(); } }

        public TreeView View { get { return _view; } set { SetView(value); } }

        public TreeDirector(TreeView view, bool showRoot)
        {
            _showRoot = showRoot;
            SetView(view);
        }
        
        public virtual void Update()
        {
            View.Nodes.Clear();
            Populate();
        }
        
        public abstract void Populate();

        public virtual object ObjectFromNode(TreeNode node)
        {
            return node.Tag;
        }

        public virtual void SetView(TreeView view)
        {
            _view = view;
            Update();
        }
    }

    public class TreeNodeLink<T>
    {
        private T _data;
        private TreeNode _node;
        private object _director;

        public TreeNode Node { 
            get { return _node; } 
            set { 
                _node = value;
                _node.Tag = this;
                Update(); 
            } 
        }

        public T Data
        {
            get { return _data; }
            set {
                _data = value;
                Update();
            }
        }

        public TreeNodeLink(object director, TreeNode node, T data)
        {
            _director = director;
            _node = node;
            _data = data;
            Update();
        }

        public void Update()
        {
            _node.Tag = this;
            if(null!=Data)
                UpdateNode();
        }

        public virtual void UpdateNode()
        {
            Node.Text = Data.ToString();
            Node.Name = Data.ToString();
        }

        public static TreeNodeLink<T> FromNode(TreeNode node)
        {
            return node.Tag as TreeNodeLink<T>;
        }
    }

    public class GeneralTreeDirector : TreeDirector
    {
        protected ITree _tree;

        public GeneralTreeDirector(TreeView view, ITree tree, bool showRoot)
            : base(view, showRoot)
        {
            SetTree(tree);
        }

        public void SetTree(ITree tree)
        {
            _tree = tree;
            Update();
        }

        public override void Populate()
        {
            if (null == _tree)
            {
                _view.Nodes.Add("(empty)");
                return;
            }

            TreeNodeCollection nodes = _view.Nodes;

            if (ShowRoot)
                nodes = AddNode(_view.Nodes, _tree).Nodes;

            PopulateChildren(nodes, _tree, True, int.MaxValue);
        }

        public ITree FromNode(TreeNode node)
        {
            return TreeNodeLink<ITree>.FromNode(node).Data;
        }

        public override object ObjectFromNode(TreeNode node)
        {
            return FromNode(node).Data;
        }

        public bool True(ITree item)
        {
            return true;
        }

        public virtual TreeNode AddNode(TreeNodeCollection nodes, ITree item)
        {
            TreeNodeLink<ITree> link = new TreeNodeLink<ITree>(this, nodes.Add(""), item);
            return link.Node;
        }

        public virtual void PopulateChildren(TreeNodeCollection nodes, ITree item, Predicate<ITree> filter, int moreDepth)
        {
            _view.BeginUpdate();
            
            int childCount = item.ChildCount;

            int loadCount = childCount;

            for (int i = 0; i < loadCount; i++)
                if (filter(item.GetChild(i)))
                {
                    TreeNode newChild = AddNode(nodes, item.GetChild(i));
                    if (moreDepth > 0)
                        PopulateChildren(newChild.Nodes, item.GetChild(i), filter, moreDepth - 1);
                }

            _view.EndUpdate();
        }
    }

    public class ObjectTreeRef
    {
        private Type _type;
        private object _ref;
        private string _name;

        public string Name { get { return _name; } }
        public Type Type{get{return _type;}}
        public object Value { get { return _ref; } }

        public ObjectTreeRef(Type type, string name, object r)
        {
            _type = type;
            _name = name;
            _ref = r;
            if (name.Equals("Me"))
            {
            }
        }
        public override string ToString()
        {
            return _name;
        }

    }

    public class ObjectTreeKeyRef:ObjectTreeRef
    {
        private Type _keyType;
        private object _key;

        public object Key { get { return _key; } }
        public Type KeyType{get{return _keyType;}}


        public ObjectTreeKeyRef(Type type, string name, object r, Type keyType, object k)
            :base(type,name,r)
        {
            _keyType = keyType;
            _key = k;
        }

    }

    public class ObjectTreeReflector
    {
        private ArrayList _used = new ArrayList();
        private List<Type> _skip = new List<Type>();

        public ObjectTreeReflector()
        {
            _skip.Add(typeof(string));
        }

        public ITree Reflect(object src, int moreDepth)
        {
            _used.Clear();
            return DoReflect(src, moreDepth);
        }

        protected ITree DoReflect(object src, int moreDepth)
        {
            ITree tree = new GeneralTree<object>(src);
            ReflectChildren(src, tree, moreDepth);
            return tree;
        }

        protected bool Show(object obj, Type type)
        {
            return !_skip.Contains(type) && !type.IsValueType;
        }

        protected void ReflectChildren(object src, ITree tree, int moreDepth)
        {
            if (_used.Contains(src))
                return;
            if(moreDepth<=0)
                return;

            _used.Add(src);

            Type type = src.GetType();
            PropertyInfo[] pis = type.GetProperties();
            foreach (PropertyInfo pi in pis)
            {
                object obj0 = null;
                Type t = pi.PropertyType;
                try
                {
                    obj0 = pi.GetValue(src, new object[0]);
                }
                catch (Exception e)
                {
                    obj0 = e.ToString();
                    t = typeof(Exception);
                }
        
                if (!Show(obj0, t))
                    continue;
                
                ITree child = new GeneralTree<object>(new ObjectTreeRef(pi.PropertyType, pi.Name, obj0));
                tree.Add(child);

                if (obj0 is IDictionary)
                {
                    IDictionary idict = obj0 as IDictionary;
                    foreach (object key in idict.Keys)
                    {
                        object value=idict[key];
                        ITree child2 = new GeneralTree<object>(
                            new ObjectTreeKeyRef(key.GetType(),string.Format("[{0}]",key),value,key.GetType(),key));
                        child.Add(child2);
                        child2.Add(DoReflect(value, moreDepth - 1));
                    }
                }
                else if (obj0 is IEnumerable)
                {
                    IEnumerable ienu = obj0 as IEnumerable;

                    int i = 0;
                    foreach (object obj in ienu)
                    {
                        ITree child2 = new GeneralTree<object>(new ObjectTreeRef(obj.GetType(), string.Format("[{0}]", i), obj));
                        child.Add(child2);
                        child2.Add(DoReflect(obj, moreDepth - 1));
                        i++;
                    }
                }
                else if(obj0!=null)
                {
                    child.Add(DoReflect(obj0, moreDepth - 1));
                }
            }
        }
    }

    public class SelectFromAttribute : Attribute
    {
        public string ID;

        public SelectFromAttribute(string id)
        {
            ID = id;
        }
    }

    public class SelectFrom
    {
        public static Dictionary<string, IValueProvider> Providers = new Dictionary<string,IValueProvider>();
    }

    public interface IValueProvider
    {
        IEnumerable GetValues();
    }

    public class ValueProvider : IValueProvider
    {
        private ICollection _coll;

        public ValueProvider(ICollection coll)
        {
            _coll = coll;
        }
        public IEnumerable GetValues() { return _coll; }
    }

    // This UITypeEditor can be associated with Int32, Double and Single
    // properties to provide a design-mode angle selection interface.
    [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    public class SelectEditor : System.Drawing.Design.UITypeEditor
    {
        public SelectEditor()
        {
        }

        // Indicates whether the UITypeEditor provides a form-based (modal) dialog, 
        // drop down dialog, or no UI outside of the properties window.
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }

        // Displays the UI for value selection.
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            // Return the value if the value is not of type Int32, Double and Single.
//            if (value.GetType() != typeof(double) && value.GetType() != typeof(float) && value.GetType() != typeof(int))
  //              return value;

            // Uses the IWindowsFormsEditorService to display a 
            // drop-down UI in the Properties window.
            IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if (edSvc != null)
            {
                ListBox lb = new ListBox();
                lb.DoubleClick += new EventHandler(lb_DoubleClick);
                IEnumerable<SelectFromAttribute> enu = context.PropertyDescriptor.Attributes.OfType<SelectFromAttribute>();
                foreach(SelectFromAttribute attr in enu)
                {
                    IValueProvider vp;
                    if(SelectFrom.Providers.TryGetValue(attr.ID, out vp))
                        foreach (object obj in vp.GetValues())
                        {
                            lb.Items.Add(obj);
                        }
                }
                
                // Display an angle selection control and retrieve the value.
                
                //lb.Items.Add()
                if (lb.Items.Count > 0)
                {
                    edSvc.DropDownControl(lb);
                    value = lb.SelectedItem;
                }
            }
            return value;
        }

        void lb_DoubleClick(object sender, EventArgs e)
        {
            (sender as ListBox).Parent.Controls.Remove(sender as ListBox);
        }

        // Draws a representation of the property's value.
        public override void PaintValue(System.Drawing.Design.PaintValueEventArgs e)
        {
            int normalX = (e.Bounds.Width / 2);
            int normalY = (e.Bounds.Height / 2);
            e.Graphics.DrawString(e.Value+"",new Font(FontFamily.GenericSansSerif,12.0f),new SolidBrush(Color.Black),new PointF(e.Bounds.Left,e.Bounds.Top));
        }

        // Indicates whether the UITypeEditor supports painting a 
        // representation of a property's value.
        public override bool GetPaintValueSupported(System.ComponentModel.ITypeDescriptorContext context)
        {
            return true;
        }
    }

    public class ExplorerDirector
    {
        private Explorer _explorer;
        private TreeDirector _treeDirector;

        protected TreeView TreeView { get { return _explorer.TreeView; } }

        public ExplorerDirector(Explorer explorer, TreeDirector treeDir)
        {
            _explorer = explorer;
            _treeDirector = treeDir;

            TreeView.AfterSelect += new TreeViewEventHandler(TreeView_AfterSelect);
            TreeView.AfterCollapse += new TreeViewEventHandler(TreeView_AfterCollapse);
        }

        void TreeView_AfterCollapse(object sender, TreeViewEventArgs e)
        {
            
        }

        void TreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            StartEdit(e.Node);
        }

        void StartEdit(TreeNode node)
        {
            object toEdit = _treeDirector.ObjectFromNode(node);
            StartEdit(toEdit);
        }

        void StartEdit(object obj)
        {
            _explorer.PropGrid.SelectedObject = obj;
        }

        public void Update()
        {
            if (null != _treeDirector)
                _treeDirector.Update();
        }

    }
}
